package com.xhj.mybatis.type;

import com.xhj.mybatis.session.Configuration;

import java.lang.reflect.Constructor;
import java.lang.reflect.Type;
import java.util.*;
import java.util.concurrent.ConcurrentHashMap;

/**
 * TypeHandlerRegistry
 *
 * @author XJks
 * @description 类型处理器注册中心
 */
public final class TypeHandlerRegistry {

    /**
     * jdbcTypeHandlerMap：用来存储每种 JdbcType（数据库类型）对应的 TypeHandler（类型处理器）。比如 JdbcType.INTEGER 对应 IntegerTypeHandler，方便通过数据库类型直接找到处理器。
     */
    private final Map<JdbcType, TypeHandler<?>> jdbcTypeHandlerMap = new EnumMap<>(JdbcType.class);

    /**
     * 当找不到合适的处理器时，兜底用的默认处理器。
     */
    private final TypeHandler<Object> unknownTypeHandler;

    /**
     * TODO:
     *  typeHandlerMap的结构是一个嵌套的Map，key是Java类型，value是另一个Map<JdbcType, TypeHandler<?>。
     *  这样设计原因如下:
     *    同一个 Java 类型，可能针对不同的数据库类型需要不同的处理器。比如 Java 类型 String.class，如果数据库字段类型是 VARCHAR，用 StringTypeHandler，如果数据库字段类型是 CLOB，用 ClobTypeHandler。
     *    所以不能只用一个处理器，而是要用一个 Map，把 Java 类型和数据库字段类型的组合都能映射到合适的处理器。
     */
    private final Map<Type, Map<JdbcType, TypeHandler<?>>> typeHandlerMap = new ConcurrentHashMap<>();


    /**
     * 存储所有注册过的 TypeHandler，方便全局查找和管理。
     */
    private final Map<Class<?>, TypeHandler<?>> allTypeHandlersMap = new HashMap<>();

    /**
     * 表示没有处理器的空映射，防止每次都 new 一个空 Map。
     */
    private static final Map<JdbcType, TypeHandler<?>> NULL_TYPE_HANDLER_MAP = Collections.emptyMap();

    /**
     * 默认构造函数，注册内置的类型处理器。
     */
    public TypeHandlerRegistry(Configuration configuration) {
        // TODO:这里需要初始化 unknownTypeHandler，兜底用的默认处理器。
        this.unknownTypeHandler = new UnknownTypeHandler(configuration);

        register(Boolean.class, new BooleanTypeHandler());
        register(boolean.class, new BooleanTypeHandler());
        register(JdbcType.BOOLEAN, new BooleanTypeHandler());
        register(JdbcType.BIT, new BooleanTypeHandler());

        register(Byte.class, new ByteTypeHandler());
        register(byte.class, new ByteTypeHandler());
        register(JdbcType.TINYINT, new ByteTypeHandler());

        register(Short.class, new ShortTypeHandler());
        register(short.class, new ShortTypeHandler());
        register(JdbcType.SMALLINT, new ShortTypeHandler());

        register(Integer.class, new IntegerTypeHandler());
        register(int.class, new IntegerTypeHandler());
        register(JdbcType.INTEGER, new IntegerTypeHandler());

        register(Long.class, new LongTypeHandler());
        register(long.class, new LongTypeHandler());

        register(Float.class, new FloatTypeHandler());
        register(float.class, new FloatTypeHandler());
        register(JdbcType.FLOAT, new FloatTypeHandler());

        register(Double.class, new DoubleTypeHandler());
        register(double.class, new DoubleTypeHandler());
        register(JdbcType.DOUBLE, new DoubleTypeHandler());

        register(String.class, new StringTypeHandler());
        register(String.class, JdbcType.CHAR, new StringTypeHandler());
        register(String.class, JdbcType.VARCHAR, new StringTypeHandler());
        register(String.class, JdbcType.LONGVARCHAR, new StringTypeHandler());
        register(JdbcType.CHAR, new StringTypeHandler());
        register(JdbcType.VARCHAR, new StringTypeHandler());
        register(JdbcType.LONGVARCHAR, new StringTypeHandler());

        // TODO:这里很重要，需要注册 unknownTypeHandler 兜底处理器。
        register(Object.class, unknownTypeHandler);
        register(Object.class, JdbcType.OTHER, unknownTypeHandler);
        register(JdbcType.OTHER, unknownTypeHandler);

        register(Date.class, new DateTypeHandler());
        register(Date.class, JdbcType.DATE, new DateOnlyTypeHandler());
        register(Date.class, JdbcType.TIME, new TimeOnlyTypeHandler());
        register(JdbcType.TIMESTAMP, new DateTypeHandler());
        register(JdbcType.DATE, new DateOnlyTypeHandler());
        register(JdbcType.TIME, new TimeOnlyTypeHandler());
    }

    /**
     * 注册一个 TypeHandler。
     * <p>
     * TODO:在源码中，这里会根据 typeHandler 的 @MappedJdbcTypes 注解自动为不同的 JdbcType 建立映射关系。这里简化处理，只注册 jdbcType 为 null 的映射。
     *
     * @param javaType    需要处理的 Java 类型
     * @param typeHandler 处理该类型的 TypeHandler 实例
     * @param <T>         泛型，表示 Java 类型
     */
    private <T> void register(Type javaType, TypeHandler<? extends T> typeHandler) {
        register(javaType, null, typeHandler);
    }

    /**
     * 注册一个 JdbcType（数据库类型）和对应的 TypeHandler（类型处理器）的映射关系。
     * 这样在处理该 JdbcType 类型的数据时，可以直接找到对应的处理器。
     *
     * @param jdbcType 数据库字段类型，比如 VARCHAR、INTEGER 等
     * @param handler  处理该类型的 TypeHandler 实例
     */
    public void register(JdbcType jdbcType, TypeHandler<?> handler) {
        // 将 jdbcType 和 handler 的对应关系放入 jdbcTypeHandlerMap 这个 Map 中
        jdbcTypeHandlerMap.put(jdbcType, handler);
    }

    /**
     * 注册一个 TypeHandler 到 typeHandlerMap 和 allTypeHandlersMap。
     *
     * @param javaType 需要处理的 Java 类型（可以为 null，表示只注册 handler）
     * @param jdbcType 需要处理的 JDBC 类型（可以为 null，表示不区分 JDBC 类型）
     * @param handler  具体的 TypeHandler 实例
     */
    private void register(Type javaType, JdbcType jdbcType, TypeHandler<?> handler) {
        // 如果指定了 javaType，则需要将 handler 注册到 typeHandlerMap
        if (javaType != null) {
            // 先从 typeHandlerMap 获取该 javaType 对应的 jdbcType-handler 映射
            Map<JdbcType, TypeHandler<?>> map = typeHandlerMap.get(javaType);
            // 如果还没有为该 javaType 注册过 handler，或者是空映射，则新建一个 HashMap
            if (map == null || map == NULL_TYPE_HANDLER_MAP) {
                map = new HashMap<>();
            }
            // 将 handler 按 jdbcType 注册到 map 中
            map.put(jdbcType, handler);
            // 更新 typeHandlerMap，确保 javaType 能找到对应的 handler
            typeHandlerMap.put(javaType, map);
        }
        // 无论 javaType 是否为 null，都要把 handler 注册到 allTypeHandlersMap，方便全局查找
        allTypeHandlersMap.put(handler.getClass(), handler);
    }

    /**
     * 根据给定的 Java 类型和 TypeHandler 类型，创建 TypeHandler 实例。
     * 优先尝试用带 Class 参数的构造方法（适用于枚举等需要传入 Java 类型的 Handler），
     * 如果没有则用无参构造方法。
     *
     * @param javaTypeClass    需要处理的 Java 类型（如 String.class、枚举类等，可以为 null）
     * @param typeHandlerClass TypeHandler 的实现类
     * @param <T>              泛型，表示 Java 类型
     * @return 创建好的 TypeHandler 实例
     */
    public <T> TypeHandler<T> getInstance(Class<?> javaTypeClass, Class<?> typeHandlerClass) {
        // 如果 javaTypeClass 不为 null，优先尝试用带 Class 参数的构造方法
        if (javaTypeClass != null) {
            try {
                // 尝试获取形如 TypeHandler(Class<?> type) 的构造方法
                Constructor<?> c = typeHandlerClass.getConstructor(Class.class);
                // 用 javaTypeClass 作为参数创建 TypeHandler 实例
                return (TypeHandler<T>) c.newInstance(javaTypeClass);
            } catch (NoSuchMethodException ignored) {
                // 如果没有这个构造方法，忽略异常，继续尝试无参构造
            } catch (Exception e) {
                // 其他异常（如反射失败），抛出异常
                throw new RuntimeException("Failed invoking constructor for handler " + typeHandlerClass, e);
            }
        }
        try {
            // 如果没有带 Class 参数的构造方法，尝试用无参构造方法创建实例
            Constructor<?> c = typeHandlerClass.getConstructor();
            return (TypeHandler<T>) c.newInstance();
        } catch (Exception e) {
            // 如果无参构造也失败，抛出异常
            throw new RuntimeException("Unable to find a usable constructor for " + typeHandlerClass, e);
        }
    }

    public boolean hasTypeHandler(Class<?> javaType) {
        return hasTypeHandler(javaType, null);
    }

    public boolean hasTypeHandler(Class<?> javaType, JdbcType jdbcType) {
        return javaType != null && getTypeHandler((Type) javaType, jdbcType) != null;
    }

    public TypeHandler<?> getMappingTypeHandler(Class<? extends TypeHandler<?>> handlerType) {
        return allTypeHandlersMap.get(handlerType);
    }

    public <T> TypeHandler<T> getTypeHandler(Class<T> type) {
        return getTypeHandler((Type) type, null);
    }

    public TypeHandler<?> getTypeHandler(JdbcType jdbcType) {
        return jdbcTypeHandlerMap.get(jdbcType);
    }

    public <T> TypeHandler<T> getTypeHandler(Class<T> type, JdbcType jdbcType) {
        return getTypeHandler((Type) type, jdbcType);
    }

    /**
     * 根据给定的 Java 类型（type）和 JDBC 类型（jdbcType）获取对应的 TypeHandler。
     * TypeHandler 用于在 Java 和数据库类型之间做转换。
     *
     * @param type     Java 类型（如 String.class、Integer.class 等）
     * @param jdbcType JDBC 类型（如 JdbcType.VARCHAR、JdbcType.INTEGER 等，可以为 null）
     * @param <T>      泛型，表示 Java 类型
     * @return 匹配到的 TypeHandler，如果没有则返回 null
     */
    private <T> TypeHandler<T> getTypeHandler(Type type, JdbcType jdbcType) {
        // 获取该 Java 类型对应的 jdbcType-handler 映射表
        Map<JdbcType, TypeHandler<?>> jdbcHandlerMap = getJdbcHandlerMap(type);
        TypeHandler<?> handler = null;
        if (jdbcHandlerMap != null) {
            // 先尝试用指定的 jdbcType 查找 handler
            handler = jdbcHandlerMap.get(jdbcType);
            if (handler == null) {
                // 如果找不到，再尝试用 jdbcType 为 null 的 handler（即不区分 jdbcType 的处理器）
                handler = jdbcHandlerMap.get(null);
            }
            if (handler == null) {
                // 如果还找不到，兜底：如果 map 里只有一个类型的 handler，就用它（pickSoleHandler 逻辑）
                handler = pickSoleHandler(jdbcHandlerMap);
            }
        }
        // 最后将 handler 强制转换为泛型类型返回
        return (TypeHandler<T>) handler;
    }

    /**
     * 根据给定的 Java 类型（type）获取对应的 JdbcType 到 TypeHandler 的映射表。
     * 这个方法会递归查找父类。
     *
     * @param type Java 类型（如 String.class、Integer.class等）
     * @return JdbcType 到 TypeHandler 的映射表，如果没有则返回 null
     */
    private Map<JdbcType, TypeHandler<?>> getJdbcHandlerMap(Type type) {
        // 从 typeHandlerMap 里直接取出该类型对应的 jdbcType-handler 映射
        Map<JdbcType, TypeHandler<?>> jdbcHandlerMap = typeHandlerMap.get(type);
        // 如果取到的是空映射（NULL_TYPE_HANDLER_MAP），说明没有处理器，直接返回 null
        if (NULL_TYPE_HANDLER_MAP.equals(jdbcHandlerMap)) {
            return null;
        }
        // 如果还没注册过该类型的 handler，并且 type 是一个类（Class），则进一步处理
        if (jdbcHandlerMap == null && type instanceof Class) {
            Class<?> clazz = (Class<?>) type;
            // TODO:在源码中，这里需要处理枚举类型 Enum.class，这里假设没有枚举类型，进行简化处理。
            // 递归查找父类有没有注册过 handler
            jdbcHandlerMap = getJdbcHandlerMapForSuperclass(clazz);
        }
        // 把查到的结果缓存到 typeHandlerMap，避免下次重复查找
        typeHandlerMap.put(type, jdbcHandlerMap == null ? NULL_TYPE_HANDLER_MAP : jdbcHandlerMap);
        // 返回最终查到的 handler 映射（可能为 null）
        return jdbcHandlerMap;
    }

    /**
     * 递归查找父类的 typeHandlerMap 映射。
     * 如果当前类没有找到对应的 TypeHandler，就去父类里找，直到 Object 为止。
     *
     * @param clazz 当前要查找的类
     * @return 父类的 JdbcType 到 TypeHandler 的映射表，如果没有则返回 null
     */
    private Map<JdbcType, TypeHandler<?>> getJdbcHandlerMapForSuperclass(Class<?> clazz) {
        // 获取当前类的父类
        Class<?> superclass = clazz.getSuperclass();
        // 如果父类为 null，或者已经到达 Object 类，说明没有更多父类可查，直接返回 null
        if (superclass == null || Object.class.equals(superclass)) {
            return null;
        }
        // 从 typeHandlerMap 中查找父类对应的 JdbcType-Handler 映射
        Map<JdbcType, TypeHandler<?>> jdbcHandlerMap = typeHandlerMap.get(superclass);
        // 如果父类有注册过 handler，直接返回
        if (jdbcHandlerMap != null) {
            return jdbcHandlerMap;
        } else {
            // 如果父类没有注册过 handler，递归继续查找更上层的父类
            return getJdbcHandlerMapForSuperclass(superclass);
        }
    }

    /**
     * 从给定的 jdbcHandlerMap（JdbcType 到 TypeHandler 的映射）中挑选唯一的 TypeHandler。
     * 如果 map 里所有的 TypeHandler 都是同一个类的实例，则返回这个 TypeHandler；
     * 如果有多个不同类型的 TypeHandler，则返回 null，表示不唯一。
     * <p>
     * 这个方法主要用于兜底场景：当找不到明确的 jdbcType 映射时，如果只有一个处理器且类型一致，就直接用它。
     *
     * @param jdbcHandlerMap JdbcType 到 TypeHandler 的映射
     * @return 唯一的 TypeHandler，如果有多个不同类型则返回 null
     */
    private TypeHandler<?> pickSoleHandler(Map<JdbcType, TypeHandler<?>> jdbcHandlerMap) {
        // soleHandler 用于记录唯一的 TypeHandler，如果发现有多个不同类型的处理器就返回 null
        TypeHandler<?> soleHandler = null;
        // 遍历 map 中所有的 TypeHandler
        for (TypeHandler<?> handler : jdbcHandlerMap.values()) {
            if (soleHandler == null) {
                // 第一次循环，先把第一个 handler 赋值给 soleHandler
                soleHandler = handler;
            } else if (!handler.getClass().equals(soleHandler.getClass())) {
                // 如果发现有不同类型的 handler，说明不唯一，直接返回 null
                return null;
            }
        }
        // 如果循环结束都没发现不同类型的 handler，返回唯一的那个 handler
        return soleHandler;
    }

}
